import signal
import ctypes
import sys
import time
import os
import threading
import queue
import uuid
import hashlib
import requests
from datetime import datetime
from PIL import Image
import traceback


# 重定向输出到日志文件
class Logger:
    def __init__(self, filename):
        self.terminal = sys.stdout
        self.log = open(filename, "a", encoding="utf-8")
        # 记录启动时间
        self.log.write(f"\n===== 程序启动于 {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} =====\n")

    def write(self, message):
        self.terminal.write(message)  # 同时输出到控制台
        self.log.write(message)  # 写入日志文件
        self.log.flush()  # 立即刷新，确保内容写入

    def flush(self):
        self.terminal.flush()
        self.log.flush()

    def close(self):
        self.log.write(f"===== 程序关闭于 {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} =====\n")
        self.log.close()


# 重定向标准输出和错误
sys.stdout = Logger("error.log")
sys.stderr = sys.stdout  # 错误输出也重定向到同一个日志文件

import webview
import pyautogui
import pyperclip
import win32api
import win32con
import tkinter as tk
import tkinter.messagebox
import keyboard
from pystray import Menu, MenuItem, Icon

# 导入自定义模块（保持原有）
import actions
import message
import user
import utils
import update
import getHomeWork

# 常量定义
ESC = 0x12
version = ''
ai_msg = ""
url = 'http://hetaojia.corp.hetao101.com/status?id={0}&crmid={1}'
status = threading.Event()  # 线程安全的状态控制
status.set()  # 初始为运行状态

# GUI操作队列（确保所有GUI操作在主线程执行）
gui_queue = queue.Queue()


def get_mac_address():
    """获取MAC地址"""
    try:
        mac = uuid.getnode()
        return ':'.join(['{:02x}'.format((mac >> i) & 0xff) for i in range(0, 48, 8)])
    except Exception as e:
        print(f"获取MAC地址出错: {str(e)}")
        print(traceback.format_exc())  # 记录详细堆栈信息
        return ""


def hash_mac_address(mac_address):
    """哈希MAC地址"""
    try:
        return hashlib.sha256(mac_address.encode()).hexdigest()
    except Exception as e:
        print(f"哈希MAC地址出错: {str(e)}")
        print(traceback.format_exc())
        return ""


def save_machine_code(code, crmid):
    """后台心跳检测（线程安全版）"""
    retry_count = 0
    max_retries = 3  # 最大重试次数
    while status.is_set():  # 使用Event判断状态，线程安全
        try:
            response = requests.get(url.format(code, crmid), timeout=10)
            content = response.content.decode('utf-8')

            # 通过队列发送GUI操作指令，由主线程处理
            if content == '1':
                gui_queue.put(('hide', None))
            elif content == '2':
                gui_queue.put(('show', None))
            elif content == '0':
                gui_queue.put(('destroy', None))

            retry_count = 0  # 成功后重置重试计数
            time.sleep(5)  # 延长检测间隔，减少资源占用
        except requests.exceptions.RequestException as e:
            print(f"请求失败: {e}")
            print(traceback.format_exc())
            retry_count += 1
            if retry_count >= max_retries:
                print("达到最大重试次数，暂停检测")
                time.sleep(30)  # 重试失败后延长等待时间
                retry_count = 0
            else:
                time.sleep(5)
        except Exception as e:
            print(f"心跳检测出错: {e}")
            print(traceback.format_exc())
            time.sleep(10)


def start_heart(crmid):
    """启动心跳线程"""
    try:
        if not crmid or crmid in [0, "None", None]:
            print("无效的crmid，不启动心跳")
            return
        code = hash_mac_address(get_mac_address())
        threading.Thread(
            target=save_machine_code,
            args=(code, crmid),
            daemon=True  # 随主线程退出
        ).start()
    except Exception as e:
        print(f"启动心跳线程出错: {e}")
        print(traceback.format_exc())


def select():
    """线程安全的文件选择"""
    try:
        # 使用队列传递文件选择请求
        file_queue = queue.Queue()
        gui_queue.put(('select_file', file_queue))
        return file_queue.get()  # 等待主线程返回结果
    except Exception as e:
        print(f"文件选择出错: {e}")
        print(traceback.format_exc())
        return ""


def process_gui_commands(window):
    """处理GUI操作队列（必须在主线程运行）"""
    while status.is_set():
        try:
            cmd, data = gui_queue.get(block=True, timeout=0.5)

            if cmd == 'hide':
                window.hide()
            elif cmd == 'show':
                window.show()
            elif cmd == 'destroy':
                window.destroy()
            elif cmd == 'select_file':
                # 执行文件选择并返回结果
                file_types = ('文本文档 (*.txt)', 'All files (*.*)')
                result = window.create_file_dialog(
                    webview.OPEN_DIALOG,
                    allow_multiple=True,
                    file_types=file_types
                )
                data.put(result[0] if result else '')
            elif cmd == 'minimize':
                window.minimize()
            elif cmd == 'restore':
                window.restore()

            gui_queue.task_done()
        except queue.Empty:
            continue
        except Exception as e:
            print(f"处理GUI指令出错: {e}")
            print(traceback.format_exc())


def qwqf():
    """无终止检查的自动操作"""
    try:
        txt_tables = utils.readtxt(select())
        for index, content in enumerate(txt_tables):
            if not content.strip():
                continue
            time.sleep(0.05)
            actions.clickp('res\searching.png', 0.8)
            time.sleep(0.05)
            pyperclip.copy(content)
            time.sleep(0.05)
            pyautogui.hotkey('ctrl', 'v')
            time.sleep(0.2)
            actions.clickp('res\choose.png', 0.8)
            time.sleep(0.05)
            actions.clickp('res\del.png', 0.95)
            print(f"{content}----------------------已完成")
    except Exception as e:
        print(f"操作出错: {e}")
        print(traceback.format_exc())
        tkinter.messagebox.showerror("错误", f"操作失败: {str(e)}")


def qf(txt):
    """带Alt键终止的循环操作"""
    terminate_flag = False

    def on_terminate_key(e):
        nonlocal terminate_flag
        if e.event_type == 'down' and e.name == 'alt':
            terminate_flag = True
            print("\n检测到Alt键，准备终止循环...")

    # 注册监听器并确保清理
    listener = keyboard.on_press_key('alt', on_terminate_key)
    print("循环已启动，按下Alt键可终止循环...")

    try:
        txt_tables = utils.readtxt(select())
        qftxt = ""
        for index, content in enumerate(txt_tables):
            if terminate_flag:
                print("循环已终止")
                return False

            arr = content.split("-")
            time.sleep(0.15)
            actions.clickp('res\search3.png', 0.8)
            time.sleep(0.15)
            pyperclip.copy(arr[0])
            time.sleep(0.15)
            pyautogui.hotkey('ctrl', 'v')
            time.sleep(0.3)
            pyautogui.moveTo(x=460, y=150, duration=0)
            pyautogui.click()
            time.sleep(0.15)
            pyautogui.moveTo(x=1000, y=950, duration=0)

            if len(arr[0]) >= 3:
                arr[0] = arr[0][-2:]

            if len(arr) > 1 and arr[1]:
                qftxt = txt.format(nickname=arr[0], parent=arr[1])
            else:
                qftxt = txt.format(nickname=arr[0])

            pyperclip.copy(qftxt)
            time.sleep(0.15)
            pyautogui.click()
            time.sleep(0.15)
            pyautogui.hotkey('ctrl', 'v')
            time.sleep(0.15)
            keyboard.wait('enter')
            print(f"{content}----------------------已完成")
            time.sleep(0.15)
    except Exception as e:
        print(f"操作出错: {e}")
        print(traceback.format_exc())
        tkinter.messagebox.showerror("错误", f"操作失败: {str(e)}")
    finally:
        keyboard.unhook(listener)  # 确保监听器被清理
    return None


def lbq():
    """另一种自动操作逻辑"""
    try:
        txt_tables = utils.readtxt(select())
        for content in txt_tables:
            if not status.is_set():  # 检查全局状态
                break
            if not content.strip():
                continue

            time.sleep(0.2)
            actions.clickp('res\searching1.png', 0.8)
            time.sleep(0.2)
            pyperclip.copy(content)
            time.sleep(0.2)
            pyautogui.hotkey('ctrl', 'v')
            time.sleep(0.85)
            actions.clickp('res\choose1.png', 0.8)
    except Exception as e:
        print(f"操作出错: {e}")
        print(traceback.format_exc())
        tkinter.messagebox.showerror("错误", f"操作失败: {str(e)}")


class AutoRun:
    """自动启动设置"""

    def __init__(self):
        try:
            name = '核桃夹'
            path = 'C:\\Program Files (x86)\\核桃夹\\核桃夹.exe'
            KeyName = 'Software\\Microsoft\\Windows\\CurrentVersion\\Run'

            key = win32api.RegOpenKey(
                win32con.HKEY_CURRENT_USER,
                KeyName,
                0,
                win32con.KEY_ALL_ACCESS
            )
            win32api.RegSetValueEx(key, name, 0, win32con.REG_SZ, path)
            win32api.RegCloseKey(key)
        except Exception as e:
            print(f'添加自动启动失败: {e}')
            print(traceback.format_exc())


class Api:
    """供前端调用的API"""

    def getstatus(self, text):
        try:
            arr = str(text).split("+")
            id1 = user.getcrmid()
            temp_data = {"user_id": arr[0], "password": '123456'}
            start_time = getHomeWork.pushcode(arr[2], id1, temp_data)
            return utils.getstatus(temp_data, start_time)
        except Exception as e:
            print(f"getstatus出错: {e}")
            print(traceback.format_exc())
            return ""

    def goupdate(self):
        try:
            update.goupdate(update.checkVersion()['version'])
            self.exit()
        except Exception as e:
            print(f"更新出错: {e}")
            print(traceback.format_exc())

    def update(self):
        try:
            res = {'version': "", 'msg': "", 'isMust': False, 'update': False, 'oldversion': ""}
            check_result = update.checkVersion()
            if check_result == -1:
                self.exit()
            res['version'] = check_result['version']
            res['msg'] = check_result['update_msg']
            res['update'] = update.updateApp()
            res['oldversion'] = update.getoldversion()
            return res
        except Exception as e:
            print(f"检查更新出错: {e}")
            print(traceback.format_exc())
            return {'update': False}

    def exit(self):
        """安全退出程序"""
        try:
            status.clear()  # 通知所有线程退出
            gui_queue.put(('destroy', None))
            # 给线程退出时间
            threading.Timer(1, lambda: os.kill(os.getpid(), signal.SIGTERM)).start()
        except Exception as e:
            print(f"退出程序出错: {e}")
            print(traceback.format_exc())

    def runclass(self, text) -> str:
        try:
            arr = str(text).split("+")
            pyperclip.copy(utils.readformatdata(arr))
            return f'pythonw {text}'
        except Exception as e:
            print(f"runclass出错: {e}")
            print(traceback.format_exc())
            return ""

    def useai(self, text) -> str:
        global ai_msg
        try:
            ai_msg = utils.getai(text)
            return ai_msg
        except Exception as e:
            print(f"useai出错: {e}")
            print(traceback.format_exc())
            return "AI处理失败"

    def copyai(self, text) -> str:
        try:
            pyperclip.copy(text)
            return ai_msg
        except Exception as e:
            print(f"copyai出错: {e}")
            print(traceback.format_exc())
            return ""

    def getworkmsg(self, txt):
        try:
            id1 = user.getcrmid()
            msg = []

            if id1 and id1 not in [0, "None"]:
                start_heart(id1)
                msg.append(utils.getAll(id1))
                msg.append(utils.getTeacherName(id1))
                return msg

            id_list = utils.readtxt("id.txt")
            if len(id_list) > 0:
                if status.is_set():
                    start_heart(id_list[0])
                msg.append(utils.getAll(id_list[0]))
                msg.append(utils.getTeacherName(id_list[0]))
                return msg
            elif txt:
                utils.writetxt("id.txt", txt)
                id_list = utils.readtxt("id.txt")
                if status.is_set():
                    start_heart(id_list[0])
                msg.append(utils.getAll(id_list[0]))
                return msg
            return ""
        except Exception as e:
            print(f"getworkmsg出错: {e}")
            print(traceback.format_exc())
            return ""

    def qf(self, program) -> str:
        try:
            gui_queue.put(('minimize', None))
            qwqf()
            return f'pythonw {program}'
        except Exception as e:
            print(f"qf出错: {e}")
            print(traceback.format_exc())
            return ""

    def greatqf(self, txt) -> str:
        try:
            gui_queue.put(('minimize', None))
            qf(txt)
            gui_queue.put(('restore', None))
            return ""
        except Exception as e:
            print(f"greatqf出错: {e}")
            print(traceback.format_exc())
            return ""

    def getVersion(self):
        try:
            ver_tables = utils.readtxt('version.htj')
            return ver_tables[-1][8:12] if ver_tables else ""
        except Exception as e:
            print(f"获取版本出错: {e}")
            print(traceback.format_exc())
            return ""

    def zq(self, program) -> str:
        try:
            gui_queue.put(('minimize', None))
            qwqf()
            print('运行')
            return f'pythonw {program}'
        except Exception as e:
            print(f"zq出错: {e}")
            print(traceback.format_exc())
            return ""

    def xh(self, program) -> str:
        try:
            gui_queue.put(('minimize', None))
            lbq()
            print('运行')
            return f'pythonw {program}'
        except Exception as e:
            print(f"xh出错: {e}")
            print(traceback.format_exc())
            return ""

    def shown(self) -> str:
        try:
            gui_queue.put(('show', None))
            return ''
        except Exception as e:
            print(f"shown出错: {e}")
            print(traceback.format_exc())
            return ''

    def quit(self) -> str:
        try:
            gui_queue.put(('hide', None))
            return ''
        except Exception as e:
            print(f"quit出错: {e}")
            print(traceback.format_exc())
            return ''

    def min(self):
        try:
            gui_queue.put(('minimize', None))
            return ''
        except Exception as e:
            print(f"min出错: {e}")
            print(traceback.format_exc())
            return ''

    def shutdown(self):
        try:
            with open('version.htj', 'a') as f:
                f.write("\nVersion-#")
        except Exception as e:
            print(f"写入版本文件出错: {e}")
            print(traceback.format_exc())
        self.exit()
        return ''

    def rmsg(self, text, header):
        try:
            message.pc_toast(header, text)
            if text == '提醒已停用':
                message.sched.shutdown(wait=False)
            else:
                message.sched.start()
            print(text)
        except Exception as e:
            print(f"消息提醒出错: {e}")
            print(traceback.format_exc())
        return ''


class MainView:
    """主视图控制（系统托盘+窗口）"""

    def __init__(self, window):
        self.window = window

    def on_show(self, icon, item):
        """系统托盘打开操作"""
        gui_queue.put(('show', None))

    def on_exit(self, icon, item):
        """系统托盘退出操作"""
        try:
            icon.stop()  # 停止托盘图标
            status.clear()  # 终止所有线程
            gui_queue.put(('destroy', None))
            # 强制退出保障
            threading.Timer(2, lambda: os.kill(os.getpid(), signal.SIGTERM)).start()
        except Exception as e:
            print(f"系统托盘退出出错: {e}")
            print(traceback.format_exc())

    def create_tray(self):
        """创建系统托盘"""
        try:
            ico = Image.open("res/maple.ico")
            menu = Menu(
                MenuItem('打开', self.on_show, default=True),
                MenuItem('退出', self.on_exit)
            )
            icon = Icon("核桃夹", ico, "核桃夹", menu)
            icon.run()
        except Exception as e:
            print(f"系统托盘创建失败: {e}")
            print(traceback.format_exc())

    def start(self):
        """启动主窗口"""
        try:
            # 启动GUI指令处理线程（主线程中）
            threading.Thread(target=process_gui_commands, args=(self.window,), daemon=True).start()
            # 启动系统托盘
            threading.Thread(target=self.create_tray, daemon=True).start()
            # 启动消息调度
            threading.Thread(target=message.sched.start, daemon=True).start()
            # 启动webview
            webview.start(localization={'global.quitConfirmation': u'确定关闭?'}, http_port=4399)
        except Exception as e:
            print(f"启动主窗口出错: {e}")
            print(traceback.format_exc())


if __name__ == "__main__":
    try:
        # 确保中文显示正常
        pyautogui.FAILSAFE = False  # 关闭安全模式，避免鼠标移到角落终止

        # 创建主窗口
        api = Api()
        window = webview.create_window(
            title='核桃夹',
            url='F:\\hetaojia\\res\\index.html',  # 可替换为实际路径
            width=1100,
            height=650,
            on_top=False,
            resizable=True,
            text_select=False,
            confirm_close=False,
            js_api=api,
            min_size=(1100, 600),
            frameless=True,
            background_color='#FFFFFF',
        )


        # 绑定窗口事件
        def on_closed():
            print('窗口已关闭')
            message.sched.shutdown(wait=False)
            status.clear()


        window.events.closed += on_closed

        # 启动自动运行设置
        AutoRun()

        # 启动应用
        main_view = MainView(window)
        main_view.start()
    except Exception as e:
        print(f"程序主入口出错: {e}")
        print(traceback.format_exc())
    finally:
        # 确保日志文件关闭
        if hasattr(sys.stdout, 'close'):
            sys.stdout.close()
